﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Threading.Tasks;

namespace Miao.ML
{
    public class WinMLSerializer
    {
        public const string defaultExtension = "txt";

        private readonly Analyzer analyzer = new Analyzer();

        public bool serializing { get; private set; }

        public static void GetSerializableDataInfo(object serializerObject, List<SerializableDataInfo> result)
        {
            result.Clear();
            Type type = serializerObject.GetType();
            FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
            foreach (FieldInfo fieldInfo in fields)
            {
                if ((!fieldInfo.IsPrivate || fieldInfo.GetCustomAttribute<WinMLSerializableAttribute>() != null) && fieldInfo.GetCustomAttribute<WinMLNonSerializableAttribute>() == null && !(fieldInfo.FieldType.BaseType == TypeCache.multicastDelegateType))
                {
                    result.Add(new SerializableFieldInfo(fieldInfo)
                    {
                        dataObject = serializerObject
                    });
                }
            }

            PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
            foreach (PropertyInfo propertyInfo in properties)
            {
                if (propertyInfo.GetCustomAttribute<WinMLSerializableAttribute>() != null && !(propertyInfo.PropertyType.BaseType == TypeCache.multicastDelegateType))
                {
                    result.Add(new SerializablePropertyInfo(propertyInfo)
                    {
                        dataObject = serializerObject
                    });
                }
            }
        }

        public static List<SerializableDataInfo> GetSerializableDataInfo(object serializerObject)
        {
            List<SerializableDataInfo> result = new List<SerializableDataInfo>();
            GetSerializableDataInfo(serializerObject, result);
            return result;
        }

        public void AddSerializationFunction<T>(Func<T, string> serializationFunction)
        {
            Func<T, string> serializationFunction2 = serializationFunction;
            analyzer.AddCustomAnalyticalFunction(typeof(T), (object value) => serializationFunction2((T)value));
        }

        public string Serialize(object serializerObject)
        {
            return analyzer.AnalyzeObject(serializerObject);
        }

        public void Serialize(object serializerObject, string savePath)
        {
            WriteToFile(Serialize(serializerObject), savePath);
        }

        public void Serialize(object serializerObject, string saveDirectory, string fileName)
        {
            Serialize(serializerObject, Path.Combine(saveDirectory, fileName + ".txt"));
        }

        public void Serialize(object serializerObject, string saveDirectory, string fileName, string extension)
        {
            Serialize(serializerObject, Path.Combine(saveDirectory, fileName + "." + extension));
        }

        public void Serialize(WinMLData winMLData, string savePath)
        {
            WriteToFile(winMLData.ToString(), savePath);
        }

        public async Task<string> SerializeAsync(object serializerObject)
        {
            object serializerObject2 = serializerObject;
            if (serializing)
            {
                throw new Exception("WinMLSerializer.SerializeAsync: 正在序列化中, 不能重复调用!");
            }

            serializing = true;
            string result = await Task.Run(() => Serialize(serializerObject2));
            serializing = false;
            return result;
        }

        public async Task SerializeAsync(object serializerObject, string savePath)
        {
            object serializerObject2 = serializerObject;
            if (serializing)
            {
                throw new Exception("WinMLSerializer.SerializeAsync: 正在序列化中, 不能重复调用!");
            }

            serializing = true;
            await WriteToFileAsync(await Task.Run(() => Serialize(serializerObject2)), savePath);
            serializing = false;
        }

        public async Task SerializeAsync(WinMLData winMLData, string savePath)
        {
            if (serializing)
            {
                throw new Exception("WinMLSerializer.SerializeAsync: 正在序列化中, 不能重复调用!");
            }

            serializing = true;
            await WriteToFileAsync(winMLData.ToString(), savePath);
            serializing = false;
        }

        public Task SerializeAsync(object serializerObject, string saveDirectory, string fileName)
        {
            return SerializeAsync(serializerObject, saveDirectory, fileName, "txt");
        }

        public Task SerializeAsync(object serializerObject, string saveDirectory, string fileName, string extension)
        {
            return SerializeAsync(serializerObject, Path.Combine(saveDirectory, fileName + "." + extension));
        }

        private void WriteToFile(string text, string savePath)
        {
            if (string.IsNullOrEmpty(savePath))
            {
                throw new Exception("WinMLSerializer.WriteToFile: savePath 无效!");
            }

            string directoryName = Path.GetDirectoryName(savePath);
            if (!Directory.Exists(directoryName))
            {
                Directory.CreateDirectory(directoryName);
            }

            using StreamWriter streamWriter = new StreamWriter(File.Open(savePath, FileMode.Create));
            streamWriter.Write(text);
        }

        private Task WriteToFileAsync(string text, string savePath)
        {
            if (string.IsNullOrEmpty(savePath))
            {
                throw new Exception("WinMLSerializer.WriteToFileAsync: savePath 无效!");
            }

            string directoryName = Path.GetDirectoryName(savePath);
            if (!Directory.Exists(directoryName))
            {
                Directory.CreateDirectory(directoryName);
            }

            using StreamWriter streamWriter = new StreamWriter(File.Open(savePath, FileMode.Create));
            return streamWriter.WriteAsync(text);
        }
    }
}
